<?php

class FiltersLoader extends AbstractLoader {

    protected function resolveObject($name) {
        $realName = $name . "Filter";
        $path = Loader::toSinglePath("ext::filters::", "");
        $this->checkFile($realName, $path);

        $filter = new $realName();

        if (!($filter instanceof Filter)) {
            throw new Exception("Expected 'Filter' implementor.");
        }

        return $filter;
    }

}

/**
 *
 */
interface Filter {
    function filter($value, array $params = array());
}

abstract class FilterBase implements Filter {

    public function filter($value, array $params = array()) {
        if (is_array($value)) {
            foreach ($value as &$v) {
                $v = $this->filter($v, $params);
            }
        } else {
            $value = $this->doFilter($value, $params);
        }

        return $value;
    }

    protected abstract function doFilter($value, array $params = array());
}

/**
 *
 * @author JeyDotC
 */
class RequestBinder {

    private $filters;
    //For future configuration capabilities.
    private $getPreffix = "get";
    private $setPreffix = "set";

    function __construct() {
        $this->filters = new FiltersLoader();
    }

    public function bind(&$object) {

        $session = ModulesContext::instance()->getSession();
        $request = ModulesContext::instance()->getRequest();

        if ($session->has("__bindings")) {

            //Gets and eliminates the bindings as they should live only on one request.
            $bindings = $session->remove("__bindings");
            $filters = $session->remove("__filters");

            foreach ($bindings as $name => $to) {
                $formValue = $request->post($name);
                $route = explode(".", $to);
                $depth = count($route);

                if ($depth >= 1) {
                    $propertyOwner = $this->resolvePropertyOwner($object, $route);
                    $propertyName = $route[$depth - 1];
                    if (is_array($filters)) {
                        $formValue = $this->filterValue($formValue, $to, $filters);
                    }
                    $propertyOwner->{$this->setPreffix . $propertyName}($formValue);
                }
            }
        }
    }

    private function filterValue($value, $to, $filters) {

        $result = $value;

        if (is_array($value)) {
            foreach ($value as $k => $v) {
                $value[$k] = $this->filterValue($v, $to, $filters);
            }
        } else {
            $valueFilters = array_key_exists($to, $filters) ? $filters[$to] : array();

            foreach ($valueFilters as $filter) {
                $result = $this->filters->load($filter["name"])->filter($result, $filter["params"]);
            }
        }
        return $result;
    }

    private function &resolvePropertyOwner($object, array $route) {
        $val = $previous = $object;

        foreach ($route as $node) {
            $previous = $val;
            if (method_exists($val, $this->getPreffix . $node)) {
                $val = &$val->{$this->getPreffix . $node}();
            } else {
                throw new Exception("Property $node not found");
            }
        }

        return $previous;
    }

}

?>
